package cn.jsu.oj.judgment.pojo;

import cn.jsu.oj.judgment.config.ResourceConfig;
import cn.jsu.oj.judgment.parsing.NoDeserialize;
import cn.jsu.oj.judgment.parsing.NoSerialize;
import cn.jsu.oj.judgment.parsing.PackAuthorize;
import cn.jsu.oj.judgment.parsing.PackType;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.Map;

/**
 * @author suixuexue, ahtonc
 */
@Slf4j
public abstract class AbstractPack {
    /**
     * 字节序，指定传递的字节数组为大端序或者小端序
     */
    private static final ByteOrder BYTE_ORDER;

    /**
     * String序列化的编码格式
     */
    private static final String CHAR_SET;

    /**
     * 包开始标志
     */
    private static final String PACK_HARD;

    /**
     * 当前包的允许的最大长度
     */
    private static final int PACK_MAX_SIZE;

    static {
        BYTE_ORDER = ResourceConfig.getConfigBoolean("parsing.pack.byteOrder", true) ?
                ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;

        CHAR_SET = ResourceConfig.getConfigString("parsing.pack.serialize.string.charset");

        PACK_HARD = ResourceConfig.getConfigString("parsing.pack.head");

        PACK_MAX_SIZE = ResourceConfig.getConfigInteger("parsing.pack.maxLength", 31457280);
    }

    /**
     * 序列化对象成byte数组
     */
    public byte[] serialize() throws Exception {
        //分配指定大小的字节缓冲区，缓冲区在被创建时就分配内存，这块内存一直被重用
        ByteBuffer byteBuffer = ByteBuffer.allocate(PACK_MAX_SIZE);
        //指定序列化的字节序列是大端序
        byteBuffer.order(BYTE_ORDER);
//        byteBuffer.putInt(PACK_HARD.length());
        byteBuffer.put(PACK_HARD.getBytes(CHAR_SET));

        //保存存放长度的位置
        int oldPosition = byteBuffer.position();
        byteBuffer.position(oldPosition + 4);

        //添加额外信息之实体类的包头信息
        byteBuffer.putShort(this.getTypeNo());

        //序列化对象
        ObjectSerialize.serialize(byteBuffer, this);

        //长度为现在长度减去oldPosition + 4
        byteBuffer.putInt(oldPosition, byteBuffer.position() - oldPosition - 3);

        byteBuffer.flip();

        byte[] array = new byte[byteBuffer.limit() + 1];
        System.arraycopy(byteBuffer.array(), 0, array, 0, byteBuffer.limit() + 1);

        return array;
    }

    /**
     * 通过ByteBuffer和class获取反序列化的对象
     * @param byteBuffer 缓冲区
     * @param clazz 反序列化类的class
     */
    private static AbstractPack getPack(ByteBuffer byteBuffer, Class<?> clazz) throws Exception {
        AbstractPack pack = (AbstractPack) clazz.getDeclaredConstructor().newInstance();

        ObjectSerialize.deserialize(byteBuffer, pack);
        return pack;
    }

    /**
     * 通过byte数组获取反序列化的对象
     * @param data 数据
     */
    public static AbstractPack deserialize(byte[] data) throws Exception {

        ByteBuffer byteBuffer = ByteBuffer.wrap(data);
        byteBuffer.order(BYTE_ORDER);
        return deserialize(byteBuffer);
    }

    /**
     * 通过ByteBuffer获取反序列化的对象
     * @param byteBuffer 缓冲区
     */
    private static AbstractPack deserialize(ByteBuffer byteBuffer) throws Exception {
        //从byteBuffer中读取实体类的包号
        short typeNo = byteBuffer.getShort();
        //获取对应已注册的类对象

        Class<?> clazz =  PackAuthorize.getInstance().getClass(typeNo);
        return getPack(byteBuffer, clazz);
    }

    /**
     * 获取包编号
     */
    public final short getTypeNo() {
        PackType packType = (PackType) this.getClass().getAnnotation(PackType.class);
        //如果该类没有使用PackType注解则会报错
        return packType.typeNo();
    }

    public ByteOrder getByteOrder() {
        return BYTE_ORDER;
    }

    public int getPackMaxSize() {
        return PACK_MAX_SIZE;
    }

    private static class ObjectSerialize {
        /**
         * 序列化实体类
         * @param buffer 缓冲区
         * @param o 实体类
         */
        public static void serialize(ByteBuffer buffer, Object o) throws Exception {
            //如果序列化Object是null则什么都不做
            if (o == null) {
                return ;
            }

            Class<?> clazz = o.getClass();

            PackType annotation = o.getClass().getAnnotation(PackType.class);

            //序列化父类属性
            for (Class<?> clz : annotation.superClass()) {
                if (!clz.isAssignableFrom(clazz)) {
                    continue;
                }
                for (Field field : clz.getDeclaredFields()) {
                    //如果有NoSerialize则不序列化
                    if (field.getAnnotation(NoSerialize.class) == null) {
                        field.setAccessible(true);
                        serializeField(buffer, field.get(o), field.getType());
                    }
                }
            }

            //序列化全部属性
            for (Field field : o.getClass().getDeclaredFields()) {
                //如果有NoSerialize则不序列化
                if(field.getAnnotation(NoSerialize.class) == null) {
                    field.setAccessible(true);
                    serializeField(buffer, field.get(o), field.getType());
                }
            }
        }

        /**
         * 序列化实体类的属性
         * @param buffer 缓冲区
         * @param o 属性的值
         * @param clazz 属性的class
         */
        private static void serializeField(ByteBuffer buffer, Object o, Class<?> clazz) throws Exception {
            //String采用特别序列化
            if (clazz == String.class) {
                serializeString(buffer, (String) o);
                return ;
            }

            //数组采用特别序列化
            if (clazz.isArray()) {
                serializeArray(buffer, o);
                return ;
            }

            //序列化基本属性
            serializeBaseField(buffer, o, clazz);
        }

        /**
         * 序列化String
         * @param buffer 缓冲区
         * @param s 字符串
         */
        private static void serializeString(ByteBuffer buffer, String s) throws Exception {
            if (s == null) {
                //String为null则将长度赋值为0
                buffer.putInt(0);
            } else {
                byte[] bs = s.getBytes(CHAR_SET);
                buffer.putInt(bs.length);
                buffer.put(bs);
            }
        }

        /**
         * 序列化数组
         * @param buffer 缓冲区
         * @param o 实体类
         */
        private static void serializeArray(ByteBuffer buffer, Object o) throws Exception {
            if (o == null) {
                buffer.putInt(0);
                return ;
            }

            int length = Array.getLength(o);
            //放入数组长度
            buffer.putInt(length);
            //获取数组成员class -> String[] String.class, int[] int.class
            Class<?> type = o.getClass().getComponentType();
            //序列化数组成员
            for (int i = 0; i < length; i++) {
                //数组成员不能为null
                if (type == String.class) {
                    serializeString(buffer, (String) Array.get(o, i));
                } else {
                    serializeBaseField(buffer, Array.get(o, i), type);
                }
            }
        }


        /**
         * 基本类型class映射,用于加快匹配
         */
        private static final Map<Class<?>, Integer> BASE_FIELD_MAP;
        static {
            BASE_FIELD_MAP = new HashMap<>();
            BASE_FIELD_MAP.put(boolean.class, 1);
            BASE_FIELD_MAP.put(Boolean.class, 1);

            BASE_FIELD_MAP.put(byte.class, 2);
            BASE_FIELD_MAP.put(Byte.class, 2);

            BASE_FIELD_MAP.put(short.class, 3);
            BASE_FIELD_MAP.put(Short.class, 3);

            BASE_FIELD_MAP.put(int.class, 4);
            BASE_FIELD_MAP.put(Integer.class, 4);

            BASE_FIELD_MAP.put(float.class, 5);
            BASE_FIELD_MAP.put(Float.class, 5);

            BASE_FIELD_MAP.put(long.class, 6);
            BASE_FIELD_MAP.put(Long.class, 6);

            BASE_FIELD_MAP.put(double.class, 7);
            BASE_FIELD_MAP.put(Double.class, 7);
        }

        /**
         * 序列化JAVA基本类型
         * @param buffer 缓冲区
         * @param o 实体类
         * @param clazz 属性的class
         */
        private static void serializeBaseField(ByteBuffer buffer, Object o, Class<?> clazz) throws Exception {
            switch (BASE_FIELD_MAP.get(clazz)) {
                case 1:
                    //boolean使用8位
                    buffer.put(((boolean) o) ? (byte) 1 : (byte) 0);
                    break;
                case 2:
                    buffer.put(((Number) o).byteValue());
                    break;
                case 3:
                    buffer.putShort(((Number) o).shortValue());
                    break;
                case 4:
                    buffer.putInt(((Number) o).intValue());
                    break;
                case 5:
                    buffer.putFloat(((Number) o).floatValue());
                    break;
                case 6:
                    buffer.putLong(((Number) o).longValue());
                    break;
                case 7:
                    buffer.putDouble(((Number) o).doubleValue());
                    break;
                default:
                    throw new Exception("没有" + clazz.getSimpleName() + "基本类型");
            }
        }

        /**
         * 解包
         * @param buffer 缓冲区
         * @param o 实体类
         */
        public static void deserialize(ByteBuffer buffer, Object o) throws Exception {
            if (o == null) {
                return ;
            }

            Class<?> clazz = o.getClass();

            PackType annotation = o.getClass().getAnnotation(PackType.class);

            for (Class<?> clz : annotation.superClass()) {
                if (!clz.isAssignableFrom(clazz)) {
                    continue;
                }
                for (Field field : clz.getDeclaredFields()) {
                    if (field.getAnnotation(NoDeserialize.class) == null) {
                        field.setAccessible(true);
                        field.set(o, deserializeField(buffer, field.getType()));
                    }
                }
            }

            for (Field field : clazz.getDeclaredFields()) {
                if (field.getAnnotation(NoDeserialize.class) == null) {
                    field.setAccessible(true);

                    field.set(o, deserializeField(buffer, field.getType()));
                }
            }
        }

        /**
         * 反序列化属性
         * @param buffer 缓冲区
         * @param clazz 属性class
         * @return  属性值
         */
        private static Object deserializeField(ByteBuffer buffer, Class<?> clazz) throws Exception {
            if (clazz == String.class) {
                return deserializeString(buffer);
            }

            if (clazz.isArray()) {
                return deserializeArray(buffer, clazz);
            }

            return deserializeBaseField(buffer, clazz);
        }

        /**
         * 反序列化字符串
         * @param buffer 缓冲区
         * @return 字符串
         * @note 字符串长度为0则默认为空字符串,不为null
         */
        private static String deserializeString(ByteBuffer buffer) throws Exception {
            String str = null;
            //获取String长度
            int length = buffer.getInt();

            if (length >= 0) {
                byte[] bytes = new byte[length];
                buffer.get(bytes);

                //转换编码
                str = new String(bytes, CHAR_SET);
            }

            return str;
        }

        /**
         * 反序列化数组
         * @param buffer 缓冲区
         * @param clazz 数组class
         * @return 数组值
         */
        private static Object deserializeArray(ByteBuffer buffer, Class<?> clazz) throws Exception {
            int length = buffer.getInt();

            if (length < 0) {
                throw new Exception("deserializeArray length < 0");
            }

            Class<?> componentType = clazz.getComponentType();
            boolean isString = componentType == String.class;

            //创建数组
            Object array = Array.newInstance(clazz.getComponentType(), length);

            for (int i = 0; i < length; i++) {
                if (isString) {
                    Array.set(array, i,  deserializeString(buffer));
                } else {
                    Array.set(array, i,  deserializeBaseField(buffer, componentType));
                }
            }

            return array;
        }

        /**
         * 反序列化基本类型
         * @param buffer 缓冲区
         * @param clazz 属性class
         * @return 属性值
         */
        private static Object deserializeBaseField(ByteBuffer buffer, Class<?> clazz) throws Exception {
            switch (BASE_FIELD_MAP.get(clazz)) {
                case 1:
                    //boolean使用8位
                    return buffer.get() == 0 ? Boolean.FALSE : Boolean.TRUE;
                case 2:
                    return buffer.get();
                case 3:
                    return buffer.getShort();
                case 4:
                    return buffer.getInt();
                case 5:
                    return buffer.getFloat();
                case 6:
                    return buffer.getLong();
                case 7:
                    return buffer.getDouble();
                default:
                    throw new Exception("没有" + clazz.getSimpleName() + "基本类型");
            }
        }
    }
}